#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/vmalloc.h>
#include <linux/gpio.h>

#define MY_BRIDGE_GPIO 504

static long power_bridge_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	switch(cmd)
	{
		case 0:
			gpio_set_value(MY_BRIDGE_GPIO, 0);
			break;
		case 1:
			gpio_set_value(MY_BRIDGE_GPIO, 1);
			break;
		default:
			break;
	}
	return 0;
}
static ssize_t power_bridge_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	char kernel_buf[32];
    long value;
    int ret;

    // 安全检查：确保用户缓冲区可读且不会溢出内核缓冲区
    if (!buf || count <= 0) {
        return -EINVAL;
    }

    // 将用户空间的数据拷贝到内核空间。count-1 是为了避免拷贝换行符（如果echo命令带了的话）
    if (copy_from_user(kernel_buf, buf, count - 1)) {
        return -EFAULT;
    }
    kernel_buf[count - 1] = '\0'; // 手动添加字符串结束符

    // 将字符串（如 "1"）转换为整数
    ret = kstrtol(kernel_buf, 10, &value);
    if (ret) {
        printk(KERN_ERR "Invalid value written to device. Please use 'echo 1' or 'echo 0'.\n");
        return -EINVAL;
    }

    // 根据转换后的整数值控制 GPIO
    if (value == 1) {
        gpio_set_value(MY_BRIDGE_GPIO, 1);
    } else if (value == 0) {
        gpio_set_value(MY_BRIDGE_GPIO, 0);
    } else {
        printk(KERN_ERR "Invalid value: %ld. Only 0 or 1 are accepted.\n", value);
        return -EINVAL;
    }

    // 返回成功写入的字节数
    return count;
}
static const struct file_operations gpio_bridge_fops={
	.owner		= THIS_MODULE,			// 代表改驱动适用于本内核
	.unlocked_ioctl = power_bridge_unlocked_ioctl,
	.write		= power_bridge_write,
};

static int power_bridge_init(void)
{
	int ret;

    // 1. 申请GPIO
    ret = gpio_request(MY_BRIDGE_GPIO, "bridgepower");
    if (ret) {
        printk(KERN_ERR "Failed to request GPIO %d, errorcode: %d\n", MY_BRIDGE_GPIO, ret);
        return ret;
    }
    // 2. 设置为输出模式，初始化为低电平（点亮LED）
    ret = gpio_direction_output(MY_BRIDGE_GPIO, 0);
    if (ret) {
        printk(KERN_ERR "Failed to set GPIO direction\n");
        gpio_free(MY_BRIDGE_GPIO); // 记得在错误路径中释放！
        return ret;
    }
    // 3. 在需要时改变电平（例如，熄灭LED）
    gpio_set_value(MY_BRIDGE_GPIO, 0);
	return 0;
}

char acCDevName[] = "power-bridge";			// 字符设备文件名称
unsigned int unMajorId, unMinorId = 0;		// 主次设备号
struct class *pClass;

int __init ctrl_bridge_init(void)
{// 注意：这边只是简单写流程，没有函数失败返回处理
	/* 1.注册，返回主设备号 */
	unMajorId = register_chrdev(0, acCDevName, &gpio_bridge_fops);
	/* 2.创建类 /sys/class/power_bridge */
	pClass = class_create(THIS_MODULE, "power_bridge");		// 若该目录存在则会返回失败
	/* 3. /dev/和/sys/class/power_bridge/ 目录下自动创建设备节点 */
	device_create(pClass, NULL, MKDEV(unMajorId, unMinorId), NULL, acCDevName);

	/* LED init */
	power_bridge_init();
    pr_info("网桥电源控制驱动成功\n");
    return 0;
}

void __exit ctrl_bridge_exit(void)
{
	gpio_free(MY_BRIDGE_GPIO);
	unregister_chrdev(unMajorId, acCDevName);
	device_destroy(pClass, MKDEV(unMajorId, unMinorId));
	class_destroy(pClass);
    printk("exit lfz chrdev driver.\n");
}

arch_initcall(ctrl_bridge_init);
module_exit(ctrl_bridge_exit);
MODULE_LICENSE("GPL");
